/*
 * Copyright 2022-2027 中国信息通信研究院云计算与大数据研究所
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.service.impl;

import java.math.BigDecimal;
import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import com.common.calculation.AmountUtil;
import com.common.calculation.InitiUtil;
import com.common.constant.AccountConstant;
import com.common.constant.ChkuplogConstant;
import com.mapper.ChkuplogMapper;
import com.mapper.Transfer;
import com.pojo.Account;
import com.pojo.Chkuplog;
import com.service.AtomicityService;

@Service
@Scope("prototype")
public class AtomicityServiceImpl extends Thread implements AtomicityService{
    @Autowired
    private ChkuplogMapper chkuplogMapper;
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    @Autowired
    private Transfer transfer;
    private final Logger logger = LoggerFactory.getLogger(AtomicityServiceImpl.class); 
    private String prex;
    /**
     * 原子性检测类型：1代表提交原子性,2代表回滚原子性
     */
    private String type;
    
    private boolean isStop = false;
    
    private int datacfg_accnum;
    
    @Override
	public void run() {
    	logger.info("原子性检测线程启动了");
    	while(true&&!isStop) {
    		int accountNum = AccountConstant.ACCOUNT_DEFAULT_NUM;
        	BigDecimal money = AmountUtil.getRandomMoney(0,1000);//转账金额
            Random r = new Random();
            String accountFrom = prex+String.valueOf(InitiUtil.getAccnum(datacfg_accnum)+((r.nextInt(accountNum )+1)));//借方
            String accountTo = prex+String.valueOf(InitiUtil.getAccnum(datacfg_accnum)+((r.nextInt(accountNum )+1)));//贷方
            while(accountFrom.equals(accountTo)) {//保证借方贷方不相等
            	accountTo = prex+String.valueOf(InitiUtil.getAccnum(datacfg_accnum)+((r.nextInt(accountNum )+1)));
            }
        	Account from = transfer.readAccount(accountFrom);
    		Account to = transfer.readAccount(accountTo);
    		if(from==null) {
    			logger.info("AtomicityServiceImpl run Account from {} is null ", accountFrom);
    			continue;
    		}
    		if(to==null) {
    			logger.info("AtomicityServiceImpl run Account to {} is null ", accountTo);
    			continue;
    		}
            if("1".equals(type)) {
            	boolean success = checkCommit(money,accountFrom,accountTo);
            	Account fromCommit = transfer.readAccount(accountFrom);
        		Account toCommit = transfer.readAccount(accountTo);
               	if(success) {
               		if(from.getAccount_bale().subtract(money).compareTo(fromCommit.getAccount_bale())==0) {
            			if(to.getAccount_bale().add(money).compareTo(toCommit.getAccount_bale())==0) {
                			logger.info("AtomicityServiceImpl checkCommit success accountFrom {} accountTo {} ", accountFrom,accountTo);
                	    	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_SUCCESS,""));
            			}else {
            				logger.info("AtomicityServiceImpl checkCommit to failure accountFrom {} accountTo {} ", accountFrom,accountTo);
                        	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl checkCommit to failure"));
            			}
            		}else {
            			logger.info("AtomicityServiceImpl checkCommit from failure accountFrom {} accountTo {} ", accountFrom,accountTo);
                    	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl checkCommit from failure"));
            		}
               	}else {
               		if(from.getAccount_bale().compareTo(fromCommit.getAccount_bale())==0) {
            			if(to.getAccount_bale().compareTo(toCommit.getAccount_bale())==0) {
                			logger.info("AtomicityServiceImpl checkUnCommit success accountFrom {} accountTo {} ", accountFrom,accountTo);
                	    	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_SUCCESS,""));
            			}else {
                			System.out.println("===>"+from.getAccount_bale());
                			System.out.println("===>"+money);
                			System.out.println("===>"+fromCommit.getAccount_bale());
                			System.out.println("===>"+to.getAccount_bale());
                			System.out.println("===>"+money);
                			System.out.println("===>"+toCommit.getAccount_bale());
            				logger.info("AtomicityServiceImpl checkUnCommit to failure accountFrom {} accountTo {} ", accountFrom,accountTo);
                        	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl checkCommit to failure"));
            			}
            		}else {
            			System.out.println("===>"+from.getAccount_bale());
            			System.out.println("===>"+money);
            			System.out.println("===>"+fromCommit.getAccount_bale());
            			System.out.println("===>"+to.getAccount_bale());
            			System.out.println("===>"+money);
            			System.out.println("===>"+toCommit.getAccount_bale());
            			logger.info("AtomicityServiceImpl checkUnCommit from failure accountFrom {} accountTo {} ", accountFrom,accountTo);
                    	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl checkCommit from failure"));
            		}
               	}
            }else if("2".equals(type)) {
            	checkUnCommit(money,accountFrom,accountTo);
            	Account fromCommit = transfer.readAccount(accountFrom);
        		Account toCommit = transfer.readAccount(accountTo);
        		if(from.getAccount_bale().compareTo(fromCommit.getAccount_bale())==0) {
        			if(to.getAccount_bale().compareTo(toCommit.getAccount_bale())==0) {
            			logger.info("AtomicityServiceImpl checkUnCommit success accountFrom {} accountTo {} ", accountFrom,accountTo);
            	    	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_SUCCESS,""));
        			}else {
        				logger.info("AtomicityServiceImpl checkUnCommit to failure accountFrom {} accountTo {} ", accountFrom,accountTo);
                    	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl checkCommit to failure"));
        			}
        		}else {
        			logger.info("AtomicityServiceImpl checkUnCommit from failure accountFrom {} accountTo {} ", accountFrom,accountTo);
                	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_A,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl checkCommit from failure"));
        		}
            }
            try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				logger.error("AtomicityServiceImpl run InterruptedException accountFrom {} accountTo {} e {}" 
						,accountFrom,accountTo,e);
			}
    	}
    	logger.info("原子性检测线程结束了");
	}

    
    
	private void checkUnCommit(BigDecimal money, String accountFrom, String accountTo) {
		TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account from = transfer.readTransactionalAccount(accountFrom);
    		if(from.getAccount_bale().subtract(money).doubleValue()<0) {
    			logger.info("AtomicityServiceImpl checkCommit Account from  {} money {} is not enough {} ",accountFrom, from.getAccount_bale(),money );
    		}else {
        		Account to = transfer.readTransactionalAccount(accountTo);
        		//更新借方余额和借方科目余额
        		Account af = new Account();
                af.setAccount_id(accountFrom);
                af.setAccount_bale(money);
                transfer.transferFrom(af);
        		logger.info("AtomicityServiceImpl checkCommit update account from {} monney {} success ", accountFrom,money);
                Account at = new Account();
                at.setAccount_id(to.getAccount_id());
                at.setAccount_bale(money);
                transfer.transferTo(at);
        		logger.info("AtomicityServiceImpl checkCommit update accoun to {} monney {} success ", accountTo,money);
    		}
	    	dataSourceTransactionManager.rollback(transactionStatus);
		} catch (Exception e) {
			logger.error("AtomicityServiceImpl checkCommit rollback accountFrom {} accountTo {} e {}" 
					,accountFrom,accountTo,e);
	    	dataSourceTransactionManager.rollback(transactionStatus);
		}		
	}



	private boolean checkCommit(BigDecimal money, String accountFrom, String accountTo) {
		boolean success = true;
		TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account from = transfer.readTransactionalAccount(accountFrom);
    		if(from.getAccount_bale().subtract(money).doubleValue()<0) {
    			logger.info("AtomicityServiceImpl checkCommit Account from  {} money {} is not enough {} ",accountFrom, from.getAccount_bale(),money );
    		}else {
        		Account to = transfer.readTransactionalAccount(accountTo);
        		//更新借方余额和借方科目余额
        		Account af = new Account();
                af.setAccount_id(accountFrom);
                af.setAccount_bale(money);
                transfer.transferFrom(af);
        		logger.info("AtomicityServiceImpl checkCommit update account from {} monney {} success ", accountFrom,money);
                Account at = new Account();
                at.setAccount_id(to.getAccount_id());
                at.setAccount_bale(money);
                transfer.transferTo(at);
        		logger.info("AtomicityServiceImpl checkCommit update accoun to {} monney {} success ", accountTo,money);
    		}
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			logger.error("AtomicityServiceImpl checkCommit rollback accountFrom {} accountTo {} e {}" 
					,accountFrom,accountTo,e);
			success = false;
	    	dataSourceTransactionManager.rollback(transactionStatus);
		}
    	return success;
	}

	/**
	 * 插入列表
	 * @param chkuplog
	 */
    public void insertChkuplog(Chkuplog chkuplog) {
    	TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		chkuplogMapper.insertChkuplog(chkuplog);
    		logger.info(" UniformityServiceImpl insertChkuplog  success ");
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			logger.error(" UniformityServiceImpl insertChkuplog  rollback e {}" ,e);
	    	dataSourceTransactionManager.rollback(transactionStatus);
		}
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}



	public boolean isStop() {
		return isStop;
	}



	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}



	public int getDatacfg_accnum() {
		return datacfg_accnum;
	}



	public void setDatacfg_accnum(int datacfg_accnum) {
		this.datacfg_accnum = datacfg_accnum;
	}



	public String getPrex() {
		return prex;
	}



	public void setPrex(String prex) {
		this.prex = prex;
	}
    
    
}